import cv2 as cv
import numpy as np

"""
    1. 在测试视频(OpenCV安装目录\sources\samples\data)上，使用基于混合高斯模型的背景提取算法，
    提取前景并显示(显示二值化图像，前景为白色)。
    2. 在1基础上，将前景目标进行分割，进一步使用不同颜色矩形框标记，并在命令行窗口中输出每个矩形框的位置和大小
    3. 安装ImageWatch，并在代码中通过设置断点，观察处理中间结果图像    
"""

videoFileName = r'C:\Users\HuangSX\Pictures\Saved Pictures\vtest.avi'
# 视频文件读取
cap = cv.VideoCapture(videoFileName)
# 构造形态学使用的kernel
kernel = cv.getStructuringElement(cv.MORPH_ELLIPSE, (3, 3))
# 获取混合高斯实例
gasModel = cv.createBackgroundSubtractorMOG2()

while True:
    # 读取视频中的图片
    ret, frame = cap.read()
    # 运用高斯模型进行拟合，在两个标准差内设置为0，在两个标准差外设置为255
    fgmk = gasModel.apply(frame)
    # 使用形态学的开运算做背景的去除
    fgmk = cv.morphologyEx(fgmk, cv.MORPH_OPEN, kernel)
    # 获取fgmk的轮廓
    contours = cv.findContours(fgmk, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)[1]
    for c in contours:
        # 进行人的轮廓判断，使用周长，符合条件的画出外接矩阵的方格
        length = cv.arcLength(c, True)
        if length > 188:
            # 获取轮廓范围
            (x, y, w, h) = cv.boundingRect(c)
            # 创建随机颜色
            color = np.random.randint(0, 255, (10, 3))
            # 使用不同颜色矩形框标记
            cv.rectangle(frame, (x, y), (x + w, y + h), color[0].tolist(), 2)
            print('矩形框坐标：(%s,%s)' % (x, y))
            print('矩形框像素大小：', w * h)
    # 进行图片的展示
    cv.imshow('fgmk', fgmk)
    cv.imshow('frame', frame)

    if cv.waitKey(150) & 0xff == 27:
        break

cap.release()
cv.destroyAllWindows()


"""
    4. 使用光流估计方法，在前述测试视频上计算特征点，进一步进行特征点光流估计
"""
# 视频文件读取
cap = cv.VideoCapture(videoFileName)

# ShiTomasi 角点检测参数
feature_params = dict(maxCorners=100,
                      qualityLevel=0.3,
                      minDistance=7,
                      blockSize=7)

# lucas kanade光流法参数
lk_params = dict(winSize=(15, 15),
                 maxLevel=2,
                 criteria=(cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 0.03))

# 创建随机颜色
color = np.random.randint(0, 255, (100, 3))
# 获取第一帧，找到角点
ret, old_frame = cap.read()
# 找到原始灰度图
old_gray = cv.cvtColor(old_frame, cv.COLOR_BGR2GRAY)

# 获取图像中的角点，返回到p0中
p0 = cv.goodFeaturesToTrack(old_gray, mask=None, **feature_params)

# 创建一个蒙版用来画轨迹
mask = np.zeros_like(old_frame)

while True:
    # 读取图像帧
    ret, frame = cap.read()
    # 灰度化
    frame_gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)

    # 计算光流
    p1, st, err = cv.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)
    # 选取好的跟踪点
    good_new = p1[st == 1]
    good_old = p0[st == 1]

    # 画出轨迹
    for i, (new, old) in enumerate(zip(good_new, good_old)):
        # 多维数据转一维,将坐标转换后赋值给a，b
        a, b = new.ravel()
        c, d = old.ravel()
        # 画直线
        mask = cv.line(mask, (a, b), (c, d), color[i].tolist(), 2)
        # 画点
        frame = cv.circle(frame, (a, b), 5, color[i].tolist(), -1)
    # 将画出的线条进行图像叠加
    img = cv.add(frame, mask)
    # 显示图像
    cv.imshow('frame', img)

    k = cv.waitKey(30) & 0xff  # 按Esc退出检测
    if k == 27:
        break

    # 更新上一帧的图像和追踪点
    old_gray = frame_gray.copy()
    p0 = good_new.reshape(-1, 1, 2)

cap.release()
cv.destoryAllWindows()
